/**
 * 
 */
package juegos;

import java.util.Vector;

/**
 * @author DaNieLooP
 *
 */
public class Cubo extends Nodo{
	/**
	 * Matriz de habitaciones del edificio
	 */
	private Habitacion [][][]cubo;
	/**
	 * coordenada x de la posicion
	 */
	private int x;
	/**
	 * coordenada y de la posicion
	 */
	private int y;
	/**
	 * coordenada z de la posicion
	 */
	private int z;
	/**
	 * dimension del edificio
	 */
	private int t;
	/**
	 * es una busqueda informada, indica cual
	 */
	private int estInf;
	/**
	 * es una busqueda no informada, indica cual
	 */
	private int estNoInf;
	
	/**
	 * Constructora del cubo
	 * @param tam dimension del cubo
	 * @param x coordenada x de la posicion actual
	 * @param y coordenada y de la posicion actual
	 * @param z coordenada z de la posicion actual
	 * @param eI indica si es informada la estrategia, cual es
	 * @param eNI indica si no es informada, cual es
	 */
	public Cubo(int tam,int x, int y,int z,int eI,int eNI){
		this.x=x;
		this.y=y;
		this.z=z;
		this.t=tam;
		this.estInf=eI;
		this.estNoInf=eNI;
		cubo = new Habitacion[t][t][t];
		for(int i=0;i<tam;i++){
			for(int j=0;j<tam;j++){
				for(int k=0;k<tam;k++){
					cubo[i][j][k]= new Habitacion();
				}
			}
		}
		this.informado=true;
		this.coste=0;
		this.profundidad = 0;
		this.camino="Te encuentras en "+"("+x+","+y+","+z+")";
		this.calculaEstado();
		this.calculaHeur();
	}
	
	/**
	 * Contructora de cubo 
	 * @param c matriz de habitaciones
	 * @param tam dimension de la matriz
	 * @param x coordenada x de la posicion del estado actual
	 * @param y coordenada y de la posicion del estado actual
	 * @param z coordenada z de la posicion del estado actual
	 * @param op operador a utilizar
	 * @param cam camino hasta este nodo
	 * @param valor valor del camino hasta ahora
	 * @param prof profundidad de este estado en el arbol de juego
	 * @param eI indica que estrategia informada usamos
	 * @param eNI indica que estrategia no informada usamos
	 * @param mensaje mensaje
	 */
	public Cubo(Habitacion [][][]c,int tam,int x,int y,int z,int op,String cam,int valor,int prof,int eI,int eNI,String mensaje){
		this.informado=true;
		this.coste=valor;
		this.profundidad=prof+1;
		this.x=x;
		this.y=y;
		this.z=z;
		this.t=tam;
		this.estInf=eI;
		this.estNoInf=eNI;
		cubo = new Habitacion[t][t][t];
		for(int i=0;i<tam;i++){
			for(int j=0;j<tam;j++){
				for(int k=0;k<tam;k++){
					cubo[i][j][k] = new Habitacion();
					cubo[i][j][k].copia(c[i][j][k]);
				}
			}
		}	
		this.calculaHeur();
		this.calculaEstado();
		String mov;
		this.camino=this.camino+"\n***************************************************";
		switch (op){
		case 1: mov = ("\nMueve arriba a "+"("+x+","+y+","+z+")"+'\n'); break;
		case 2: mov = ("\nMueve abajo a "+"("+x+","+y+","+z+")"+'\n'); break;
		case 3: mov = ("\nMueve izquierda a "+"("+x+","+y+","+z+")"+'\n'); break;
		case 4: mov = ("\nMueve derecha a "+"("+x+","+y+","+z+")"+'\n'); break;	
		case 5: mov = ("\nMueve delante a "+"("+x+","+y+","+z+")"+'\n'); break;
		case 6: mov = ("\nMueve detras a "+"("+x+","+y+","+z+")"+'\n'); break;
		case 11: mov = ("\nAbre arriba en "+"("+x+","+y+","+z+")"+'\n'); break;
		case 12: mov = ("\nAbre abajo en "+"("+x+","+y+","+z+")"+'\n'); break;
		case 13: mov = ("\nAbre izquierda en "+"("+x+","+y+","+z+")"+'\n'); break;
		case 14: mov = ("\nAbre derecha en "+"("+x+","+y+","+z+")"+'\n'); break;
		case 15: mov = ("\nAbre delante en "+"("+x+","+y+","+z+")"+'\n'); break;
		case 16: mov = ("\nAbre detras en "+"("+x+","+y+","+z+")"+'\n'); break;
		default: mov = ("\nEste movimiento no existe."+'\n');
		}		
		this.camino = (cam +mensaje+mov);
		this.camino=this.camino+"\n***************************************************";
	}
	
	
	
	/**La heuristica es la minima distancia a una de las esquinas. 
	 * 
	 * @return devuelve el valor de la menor distancia a una esquina
	 */
	public int calculavHeur(){
		int vHe=0;
		for(int i=0;i<t;i++){
			for(int j=0;j<t;j++){
				for(int k=0;k<t;k++){
					vHe+=this.cubo[i][j][k].valorHeur();
				}
			}
		}
		int arrizdetr,arrizdel,arrderdetr,arrderdel;
		int abizdetr,abizdel,abderdetr,abderdel;
		int valor;
		int ta = this.t-1;
		abizdetr = this.x+this.y+this.z;
		abizdel = this.x+this.y+ta-this.z;
		abderdetr = ta-this.x+this.y+this.z;
		abderdel = ta-this.x+this.y+ta-this.z;
		arrizdetr = this.x+ta-this.y+this.z;
		arrizdel = this.x+ta-this.y+ta-this.z;
		arrderdetr = ta-this.x+ta-this.y+this.z;
		arrderdel = ta-this.x+ta-this.y+ta-this.z;
		if(abizdetr<abizdel){
			valor = abizdetr;
		}else{
			valor=abizdel;
		}
		if(abderdetr<valor){
			valor = abderdetr;
		}
		if(abderdel<valor){
			valor = abderdel;
		}
		if(arrizdetr<valor){
			valor = arrizdetr;
		}
		if(arrizdel<valor){
			valor = arrizdel;
		}
		if(arrderdetr<valor){
			valor = arrderdetr;
		}
		if(arrderdel<valor){
			valor = arrderdel;
		}
		valor=valor*5+vHe;
		if(x==0){
			if(y==0){
				if(z==0||z==t-1){
					valor=0;
				}
			}else if(y==t-1){
				if(z==0||z==t-1){
					valor=0;
				}
			}
		}else if(x==t-1){
			if(y==0){
				if(z==0||z==t-1){
					valor=0;
				}
			}else if(y==t-1){
				if(z==0||z==t-1){
					valor=0;
				}
			}
		}
		return valor;
	}
	
	/**
	 * Calcula el valor heuristico del estado
	 */
	public void calculaHeur(){
		this.vHeur=calculavHeur();
	}
	
	/**
	 * Calcula el valor del estado
	 */
	public void calculaEstado(){
		long aux = 0;
		long cont=x*100+y*10+z;
		cont=cont*12;
		for(int i=0;i<t;i++)
			for(int j=0;j<t;j++)
				for(int k=0;k<t;k++){
					aux=aux+(cubo[i][j][k].getEstadoHabitacion()+cont);
					cont=cont*12;
				}
		
		this.estado=aux;
	}
	
	
	/**
	 * Indica si estamos en la salida del cubo
	 */
	public boolean esFinal(){  
		return vHeur==0;
    }
	/**
	 * Metodo que mueve en una direccion
	 * @param dir direccion en la que moveremos si se puede
	 * @return nueva configuracion del cubo
	 */
	private Cubo mueve(int dir){    
		Cubo c = null;
		if(dir==1){
			if(cubo[x][y][z].isAbierta("arriba")==true){
				c = new Cubo(cubo,t,x,y+1,z,1,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}else if(dir==2){
			if(cubo[x][y][z].isAbierta("abajo")==true){
				c = new Cubo(cubo,t,x,y-1,z,2,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}else if(dir==3){
			if(cubo[x][y][z].isAbierta("izquierda")==true){
				c = new Cubo(cubo,t,x-1,y,z,3,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}else if(dir==4){
			if(cubo[x][y][z].isAbierta("derecha")==true){
				c = new Cubo(cubo,t,x+1,y,z,4,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}else if(dir==5){
			if(cubo[x][y][z].isAbierta("delante")==true){
				c = new Cubo(cubo,t,x,y,z+1,5,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}else if(dir==6){
			if(cubo[x][y][z].isAbierta("detras")==true){
				c = new Cubo(cubo,t,x,y,z-1,6,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,"");
			}
		}
		return c;
	}
	
	/**
	 * Metodo que abre una puerta si puede
	 * @param dir direccion de la puerta a abrir
	 * @return nueva configuracion del cubo
	 */
	private Cubo abre(int dir){
		Cubo c = null;
		if(dir==1){
			if(y<t-1){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,11,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir arriba..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("arriba");
					c.cubo[x][y+1][z].abre("abajo");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir arriba..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,11,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}else if(dir==2){
			if(y>0){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,12,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir abajo..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("abajo");
					c.cubo[x][y-1][z].abre("arriba");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir abajo..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,12,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}else if(dir==3){
			if(x>0){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,13,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir izquierda..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("izquierda");
					c.cubo[x-1][y][z].abre("derecha");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir izquierda..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,13,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}else if(dir==4){
			if(x<t-1){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,14,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir derecha..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("derecha");
					c.cubo[x+1][y][z].abre("izquierda");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir derecha..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,14,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}else if(dir==5){
			if(z<t-1){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,15,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir delante..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("delante");
					c.cubo[x][y][z+1].abre("detras");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir delante..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,15,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}else if(dir==6){
			if(z>0){
				Juego j = new Juego(this.estInf,this.estNoInf);
				if(j.getNResultado()!=null){
					c = new Cubo(cubo,t,x,y,z,16,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,'\n'+"intentas abrir detras..."+'\n'+j.getNombre());
					c.cubo[x][y][z].abre("detras");
					c.cubo[x][y][z-1].abre("delante");
					c.calculaEstado();
				}else{
					this.camino = this.camino + '\n'+"intentas abrir detras..."+'\n';
					this.camino = this.camino+'\n'+j.getNombre()+'\n'+"no conseguiste abrirla"+'\n';
					this.camino=this.camino+"\n***************************************************";
					c = new Cubo(cubo,t,x,y,z,16,this.camino,this.coste+1,this.profundidad,this.estInf,this.estNoInf,j.getNombre());
				}
			}
		}
		return c;
	}
	
	/**
	 * Metodo que genera todos los hijos del estado actual
	 * @return un vector con todos los hijos.
	 */
	public Vector generaHijos(){
    	Vector nuevo=new Vector(0,1);
    	Cubo op1 = this.mueve(1);
    	Cubo op2 = this.mueve(2);
    	Cubo op3 = this.mueve(3);
    	Cubo op4 = this.mueve(4);
    	Cubo op5 = this.mueve(5);
    	Cubo op6 = this.mueve(6);
    	Cubo op7 = this.abre(1);
    	Cubo op8 = this.abre(2);
    	Cubo op9 = this.abre(3);
    	Cubo op10 = this.abre(4);
    	Cubo op11 = this.abre(5);
    	Cubo op12 = this.abre(6);
    	if (op1!=null) nuevo.addElement(op1);
        if (op2!=null) nuevo.addElement(op2);
      	if (op3!=null) nuevo.addElement(op3);
      	if (op4!=null) nuevo.addElement(op4); 
      	if (op5!=null) nuevo.addElement(op5);
        if (op6!=null) nuevo.addElement(op6);
      	if (op7!=null) nuevo.addElement(op7);
      	if (op8!=null) nuevo.addElement(op8); 
      	if (op9!=null) nuevo.addElement(op9);
        if (op10!=null) nuevo.addElement(op10);
      	if (op11!=null) nuevo.addElement(op11);
      	if (op12!=null) nuevo.addElement(op12); 
        return nuevo;
	} 
	
}
